
#include "udp_server.h"
#include "channel.h"
#include "loop_event.hpp"
#include "data_buffer.h"
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <memory>
#include <sys/epoll.h>

UdpServer::UdpServer(LoopEvent *loop_event)
{
    m_loop_event = loop_event;
}

UdpServer::~UdpServer()
{
    if (m_channel)
    {
        m_channel->RemoveEvent();
    }
}

void UdpServer::Listen(int16_t port)
{
    m_udp_socket.Open();
    m_udp_socket.SetOpt();
    m_udp_socket.SetNonBlocking();

    struct sockaddr_in sai;
    memset(&sai, 0, sizeof(sai));

    sai.sin_port = htons(port);
    sai.sin_addr.s_addr = INADDR_ANY;
    sai.sin_family = AF_INET;

    assert(bind(m_udp_socket.GetSocketFd(), (struct sockaddr*)&sai, sizeof(sai)) == 0);

    m_channel = std::shared_ptr<Channel>(new Channel(m_loop_event, m_udp_socket.GetSocketFd(), nullptr, std::bind(&UdpServer::DoReadFunc, this, std::placeholders::_1), nullptr));
    // add oneshot
    m_channel->AddEvent(EPOLLIN | EPOLLONESHOT);
}

void UdpServer::DoRead()
{
    struct sockaddr_in remote_sai;
    socklen_t remote_len = sizeof(remote_sai);
    memset(&remote_sai, 0, sizeof(remote_sai));

    int size = 0;
    do
    {
        memset(m_buffer, 0, sizeof(m_buffer));
        memset(&remote_sai, 0, sizeof(remote_sai));
        size = recvfrom(
                m_udp_socket.GetSocketFd(),
                m_buffer, sizeof(m_buffer) - 1, 0,
                (struct sockaddr *) &remote_sai,&remote_len);

        if (size <= 0)
        {
            break;
        }
        // process receive message data
        if (m_onMessage)
        {
            UdpPacket packet;
            packet.data   = m_buffer;
            packet.length = size;
            packet.sai    = remote_sai;
            m_onMessage(&packet);
        }

    } while(true);

    m_channel->UpdateEvent(EPOLLIN | EPOLLONESHOT);

}

void UdpServer::DoReadFunc(int64_t channel_id)
{
    DoRead();
}

int UdpServer::SendMsg(UdpBufferPacket &packet, const uint8_t *data, int length)
{
    return packet.buffer->WriteData(data, length, packet.sai, packet.sai_len);
}

void UdpServer::SetUdpMessage(OnUdpMessage && message_func)
{
    m_onMessage = message_func;
}
